package tests;

import network.*;
import util.Logger;
import util.StopWatch;

import java.util.ArrayList;

public class Test {
    public static Network n = I2Network.getNetwork();
    public static Logger logger = new Logger();

    public static void main(String[] args) throws InterruptedException {
        n.initializeModelAndRules(0, 0);
        test2();
    }

    public static void test0() {
        System.out.println("# rules " + n.getInitialRules().size());

        StopWatch.StopWatch();
        for (Rule rule : n.getInitialRules()) {
            new Rule(rule.getDevice(), rule.getDstIp(), rule.getPriority(), rule.getPort());
        }
        System.out.println("Scan every rule " + (StopWatch.getElapsedTimeUS() / n.getInitialRules().size()) + " us per-rule");

        StopWatch.StopWatch();
        for (Rule rule : n.getInitialRules()) {
            n.getModel().bddEngine.encodeDstIPPrefix(rule.getDstIp(), rule.getPriority());
        }
        System.out.println("Encode every rule's match " + (StopWatch.getElapsedTimeUS() / n.getInitialRules().size()) + " us per-rule");
    }

    public static void test1() {
        for(Rule rule : n.getInitialRules()) {
            ArrayList<Rule> miniBatch = new ArrayList<>();
            miniBatch.add(rule);

            StopWatch.StopWatch();
            Changes changes = new Changes(n.getModel().bddEngine);
            n.insertMiniBatch(miniBatch, changes);
            logger.addPerUpdateTime("Rule => Change", StopWatch.getElapsedTimeUS());

            StopWatch.StopWatch();
            n.getModel().update(changes);
            logger.addPerUpdateTime("Change => Transferred ECs", StopWatch.getElapsedTimeUS());
        }

        System.out.println("Total #EC: " + n.getModel().portsToPred.size());
        logger.displayAllTime();
    }

    public static void test2() {
        StopWatch.StopWatch();
        Changes changes = new Changes(n.getModel().bddEngine);
        n.insertMiniBatch(n.getInitialRules(), changes);
        System.out.println("Rule => Change " + (StopWatch.getElapsedTimeUS() / n.getInitialRules().size()) + " us");

        System.out.println("Number of changes " + changes.size());
        System.out.println("Number of different BDD's among changes " + changes.bdds());

        StopWatch.StopWatch();
        n.getModel().update(changes);
        System.out.println("Change => Transferred ECs " + (StopWatch.getElapsedTimeUS() / n.getInitialRules().size()) + " us");

        System.out.println("Total #EC: " + n.getModel().portsToPred.size());
    }

    public static void test4(int nThreads) {
        ArrayList<Thread> threads = new ArrayList<>();
        ArrayList<Changes> listChanges = new ArrayList<>();
        for (int i = 0; i < nThreads; i++) {
            Changes tt = new Changes(n.getModel().bddEngine);
            listChanges.add(tt);
            Thread t = new Stage1Thread(nThreads, i, n.getInitialRules(), n, tt);
            threads.add(t);
        }

        StopWatch.StopWatch();
        for (Thread t : threads) {
            t.start();
        }
        try {
            for (Thread t : threads) {
                t.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Stage 1 finish time " + (StopWatch.getElapsedTimeUS() / n.getInitialRules().size()) + " us");

        StopWatch.StopWatch();
        Changes changes = new Changes(n.getModel().bddEngine);
        for (Changes i : listChanges) {
            changes.merge(i);
        }
        System.out.println("Number of changes " + changes.size());
        double t = StopWatch.getElapsedTimeUS();
        System.out.println("Merge time " + (t / n.getInitialRules().size()) + " us");
        System.out.println(t + " in total");

        StopWatch.StopWatch();
        n.getModel().update(changes);
        System.out.println("Change => Transferred ECs " + (StopWatch.getElapsedTimeUS() / n.getInitialRules().size()) + " us");

        System.out.println("Total #EC: " + n.getModel().portsToPred.size());
    }
}
